﻿if (typeof jQuery === 'undefined') {
    throw new Error('jquery.field JavaScript requires jQuery');
}

if (typeof Plugin === 'undefined') {
    throw new Error('jquery.field JavaScript requires Plugin');
}


(function ($) {
    'use strict';

    var Field = function (el, options) {
        this.$el = $(el);
        this.namespace = _config_.field.namespace;
        this.methods = _config_.field.methods;
        this.events = _config_.field.events;
        this.defaults = $.extend(true, {}, _config_.field.defaults);

        this.defaults.type = (this.$el.prop('type') || this.$el.attr('type') || this.$el.attr('data-type') || this.$el.prop('tagName') || this.defaults.type).toLowerCase();

        if (!this.$el.is('[type]')) {
            this.$el.attr('type', this.defaults.type);
        }
        if (!this.$el.is('[data-type]')) {
            this.$el.attr('data-type', this.defaults.type);
        }

        //form 取 data-form 指定元素或者 第一个被标记为 data-toggle="form" 的父元素
        this.$form = this.$el.attr(_config_.attribute.formAttr);
        if ($.com.hasValue(this.$form)) {
            this.$form = $(this.$form).first();
        } else {
            this.$form = this.$el.closest(_config_.selector.form);
        }

        if (this.$el.is('[pattern]') && !this.$el.is('[data-pattern]')) {
            this.$el.attr('data-pattern', this.$el.attr('pattern'));
        }
        if (this.$el.is('[role]') && !this.$el.is('[data-role]')) {
            this.$el.attr('data-role', this.$el.attr('role'));
        }

        //initials 阶段
        //field 初始值合并form元素值, 默认值, 和field元素值
        this.initials = $.com.getDataBindFromElement(this.$el);
        this.initials.type = this.initials.type || this.defaults.type;
        this.initials.type = this.initials.type.toLowerCase();
        this.initials.role = this.initials.role || this.initials.type;
        this.initFormat();

        if (this.initials.dataType === 'date' || $.com.hasValue(this.initials.popup)) {
            this.initials.readonly = $.com.hasValue(this.initials.readonly) ? this.initials.readonly : true;
        }

        if ($.com.hasValue(this.initials.pattern) && typeof (this.initials.pattern) === 'string' && this.initials.pattern.includes(',')) {
            this.initials.pattern = $.com.predefinedRegular(this.initials.pattern);
        }

        if (!this.$el.is('[pattern]') && $.com.hasValue(this.initials.pattern)) {
            this.$el.attr('pattern', this.initials.pattern);
        }

        if (!this.$el.is('[format]') && $.com.hasValue(this.initials.format)) {
            this.$el.attr('format', this.initials.format);
        }
        this.$el.attr('data-format', this.initials.format);

        if (!$.com.isArray(this.initials.dataSource) && (this.initials.role === 'kendoDropDownList' || this.initials.role === 'kendoMultiSelect')) {
            this.initials.dataSource = $.extend(true, this.initials.dataSource, _config_.dataSource.noPaging);
        }

        this.initials = $.extend(true, {}, _config_.field.initials, this.initials);

        if ($.com.hasValue(this.initials.template)) {
            this.initials.template = $(this.initials.template);
        } else if (this.initials.role === 'kendoUpload') {
            this.initUploader();
        }

        this.field = this.initials.field;

        if (this.initials.withAttachments === true) {
            this.initials.withAttachments = this.initials.single ? "Attachments" : this.initials.field;
        }

        if (this.initials.type === 'hidden') {
            this.initials.fieldset = false;
            this.initials.display = false;
        }

        if ($.com.hasValue(this.initials.popup) && !this.isDetail()) {
            this.initials.icon = 'fa fa-search';
            this.$el.data('icon', this.initials.icon);
            this.$el.attr('data-icon', this.initials.icon);
        }

        //originals 阶段
        this.originals = $.extend(true, {}, this.defaults, this.initials);
        this.originals.dataType = this.originals.dataType.toLowerCase();
        this.originals.side = this.originals.side.toLowerCase();
        this.originals.label = this.originals.label || this.field;
        this.originals.text = this.originals.text || this.originals.label;
        this.originals.name = this.$el.attr('name') || this.originals.name || this.originals.label;
        this.originals.title = this.originals.title || this.originals.label;
        this.originals.value = this.$el.val() || this.$el.attr('value') || this.originals.value;
        this.originals.required = this.$el.is('[required]') || this.$el.is('.required') || this.originals.required;
        this.originals.message = this.originals.message || '请输入' + this.originals.label;
        this.originals.placeholder = this.$el.prop('placeholder') || this.originals.placeholder || this.originals.label;

        //获取从 form 继承来的属性, 如果元素本身没有设置以下属性, 则优先取 form 的, 其次取默认值
        if ($.com.isJqueryObject(this.$form)) {
            let form = this.$form.data(_config_.form.namespace);
            if ($.com.hasValue(form)) {
                if (!$.com.hasValue(this.initials.url)) {
                    this.originals.url = form.options.url;
                }
                if (!$.com.hasValue(this.initials.size)) {
                    this.originals.size = form.options.size || this.defaults.size;
                }
                if (!$.com.hasValue(this.initials.alwaysInitial)) {
                    this.originals.alwaysInitial = form.options.alwaysInitial || this.defaults.alwaysInitial;
                }
            }
        }

        //options 阶段
        this.options = $.extend(true, {}, this.originals, options);
        this.options.serial = $.com.getSerial(16);
        //处理id
        this.getNamedPrefix();

        if (this.initials.role === 'kendoUpload' && this.initials.button !== 'import') {
            this.initials.template = this.initials.uploadTemplate;
        }

        this.init();
    };

    Field.prototype = new Plugin();

    Field.prototype.init = function () {
        this.options.phase = 'initial';
        this.initFieldset();
        this.initEvent();
        this.initData();
        //this.refresh();
    };

    Field.prototype.initFieldset = function () {
        //first time
        if ($.com.hasValue(this.options.fieldset) && this.options.fieldset && !$.com.isJqueryObject(this.$fieldset) && $.com.hasValue(this.options.template)) {
            this.$fieldset = $(this.options.template).children().clone();
            this.$el.after(this.$fieldset);
            //label
            var $title = this.$fieldset.find('.title');
            $title.find('label').html(this.options.label).attr('for', this.options.field);
            if (this.isDetail()) {
                $title.find('.required').remove();
            } else {
                //required
                if (this.options.required) {
                    $title.find('.required').show();
                }
            }

            if (this.options.tooltip) {
                $title.find('.tooltips').show().attr('data-placement', this.options.tooltipPlacement).attr('title', this.options.tooltipTitle);
            }

            //field
            var $input = this.$fieldset.find('.field');
            $input.prepend(this.$el);

            //role
            if (this.options.role === 'span' || this.options.role === 'label' || this.options.role === 'checkbox' || this.options.role === 'radio') {
                this.$fieldset.addClass('narrow');
            }

            //layout
            if ($.com.hasValue(this.options.layout)) {
                if (this.options.layout.startsWith('horizontal')) {
                    this.$fieldset.addClass('horizontal');
                    if (this.options.layout.endsWith('r2l')) {
                        $input.after($title);
                        $title.addClass('r2l');
                    }
                } else {
                    this.$fieldset.addClass('vertical');
                    if (this.options.layout.endsWith('t2b')) {
                        $input.after($title);
                    }
                }
            }

            if ($.com.hasValue(this.options.icon) && $.com.hasValue(this.options.iconTemplate) && !this.isDetail()) {
                var icon = $(this.options.iconTemplate).children().clone();
                icon.find('.icon').addClass(this.options.icon);
                this.$el.after(icon);
                this.$el.css('padding-right', '36px');
                if ($.com.hasValue(this.options.iconClicking) || $.com.hasValue(this.options.iconClicked)) {
                    icon.css('cursor', 'pointer');
                }
            }

            //size
            if ($.com.hasValue(this.options.size)) {
                var size = this.options.size.toString().split(',');
                var size0 = 3, size1 = 12, size2 = 12;
                if ($.com.hasValue(this.options.layout) && this.options.layout.startsWith('horizontal')) {
                    size0 = 3;
                    size1 = 3;
                    size2 = 9;
                }
                if (size.length > 0) {
                    size0 = Number(size[0].trim());
                }
                if (size.length > 1) {
                    size1 = Number(size[1].trim());
                }
                if (size.length > 2) {
                    size2 = Number(size[2].trim());
                }

                this.$fieldset.hide();
                if (size0 !== 0) {
                    this.$fieldset.show().addClass('col-md-' + size0);

                    var label = this.$fieldset.find('.title').hide();
                    if (size1 !== 0) {
                        label.show().addClass('col-md-' + size1);
                    }
                    var field = this.$fieldset.find('.field').hide();
                    if (size2 !== 0) {
                        field.show().addClass('col-md-' + size2);
                    }
                }
            }
        }
    };

    Field.prototype.initInput = function () {
        if (!$.com.hasValue(this.options.url) && $.com.isJqueryObject(this.$form)) {
            var form = this.$form.data(_config_.form.namespace);
            if ($.com.hasValue(form)) {
                this.options.url = form.options.url;
            }
        }

        this.execute(this.options.loading);

        //draw Field
        if (!this.isDetail()) {
            this.$el.attr('name', this.options.name);
            if ($.com.isJqueryObject(this.$fieldset)) {
                this.$fieldset.find(_config_.selector.inputInvalidMsg).attr('data-for', this.options.name);
            }

            this.role = this.$el.data(this.options.role);
            if ($.com.hasValue(this.options.role) && (this.options.role.startsWith('kendo') || this.options.role === 'tagsinput')) {
                if (!$.com.hasValue(this.role)) {
                    var fn = 'jQuery.fn.' + this.options.role;
                    fn = $.com.getFunction(fn);
                    if ($.isFunction(fn)) {
                        fn.call(this.$el, this.initials);
                        this.role = this.$el.data(this.options.role);
                        if ($.com.hasValue(this.role)) {
                            this.initials.dataSource = $.extend(true, { data: this.options.data }, this.role.options.dataSource);
                            this.role[_config_.field.namespace] = this;
                            this.role.initials = { dataSource: this.initials.dataSource };
                            if ($.com.isJqueryObject(this.$fieldset)) {
                                this.$fieldset.find('.k-widget').not('.k-editor-widget').css('width', '100%');
                            }
                        }
                    }
                } else if (this.options.type === 'file' || this.options.type === 'img') {
                    if (this.options.type === 'file') {
                        this.role.clearAllFiles();
                    } else if (this.options.type === 'img') {
                        this.$el.empty();
                    }

                    var formData = this.getFormData();
                    var withAttachments = $.com.hasValue(this.options.withAttachments) && $.com.hasValue(formData[this.options.dataKeyField]);
                    if (withAttachments) {
                        if ($.com.hasValue(formData[this.options.withAttachments]) && this.options.type === 'file') {
                            if (this.options.type === 'file') {
                                this.role._renderInitialFiles(this.options.data);
                            } else if (this.options.type === 'img') {
                                this.$el.empty();
                                let $viewer = this.$el.append('<ul class="image-viewer"></ul>').children().first();
                                for (var index = 0; index < this.options.data.length; index++) {
                                    const element = this.options.data[index];
                                    $viewer.append(`<li><img src="${$.com.getImageFileName(element.Path)}" alt="${element.Name}"></li>`);
                                }

                                $viewer.viewer();
                            }

                            this.execute(this.options.loaded)
                        } else {
                            this.getAttachments(() => this.execute(this.options.loaded));
                        }
                    }
                }
            } else {
                if (this.options.role === 'checkbox' || this.options.role === 'radio') {
                    var $template;
                    if (this.options.role === 'checkbox') {
                        $template = $('#__TemplateCheckbox');
                    } else {
                        $template = $('#__TemplateRadio');
                    }

                    if ($template.hasValue() && $template.children().hasValue()) {
                        $template = $template.children();
                        if (this.options.withGroup && $.com.isArray(this.options.data)) {
                            for (var i = 0; i < this.options.data.length; i++) {
                                var $temp = $template.clone();
                                if (this.options.inline) {
                                    $temp.addClass(this.options.role + '-inline');
                                }
                                this.$el.append($temp);
                                var data = this.options.data[i];
                                var id = data[this.options.dataValueField] || data[this.options.dataKeyField];
                                $temp.find('input').attr('id', this.options.serial + '_' + id).attr(_config_.attribute.id, id);
                                $temp.find('label').attr('for', this.options.serial + '_' + id).html(data[this.options.dataTextField]);
                            }
                        } else {
                            this.$el.addClass(this.options.role);
                            this.$el.append($template.children().clone());
                            this.$el.find('input').attr('id', this.options.serial);
                            this.$el.find('label').attr('for', this.options.serial).html(this.options.text);
                        }

                        this.$el.find('input').attr('name', this.options.id);
                    }
                } else if (this.options.type !== 'img') {
                    this.$el.addClass('k-textbox').css('width', '100%');
                }
            }

            this.initValidator();

            if ($.com.hasValue(this.options.next) && typeof this.options.next === 'string') {
                this.getSingleNext();
            }

            this.readonly();
        }

        this.display();
        if (this.options.side === 'server' && $.com.hasValue(this.options.url)) {
            this.refresh(this.options.value);
        }

        this.execute(this.options.loaded);
    };

    Field.prototype.reInit = function () {
        //this.$el.fieldset();
        this.initData();
        this.refresh();
    };

    Field.prototype.initData = function () {
        this.execute(this.options.binding);

        if (this.options.side === 'server' && $.com.hasValue(this.options.url)) {
            $.get(this.options.url, data => {
                if ($.isFunction(this.options.dataAdapter)) {
                    this.options.data = this.options.dataAdapter(data);
                    this.options.dataSource = { data: this.options.data };
                    this.initials.dataSource = this.options.dataSource;
                }

                this.execute(this.options.bound);

                this.initInput();
            });
        } else {
            this.execute(this.options.bound);

            this.initInput();
        }
    };

    Field.prototype.initEvent = function () {
        var that = this;

        this.$el.click(function (event) {
            that.onClick(event);
            event.stopPropagation();
        });

        this.$el.change(function (event) {
            that.onChange(event);
            event.stopPropagation();
        });

        this.$el.blur(function (event) {
            //that.onChange(event);
            //event.stopPropagation();
        });

        this.$el.select(function (event) {
            that.onSelect(event);
            event.stopPropagation();
        });

        this.$el.keyup(function (event) {
            that.onKeyUp(event);
            event.stopPropagation();
        });

        this.$el.keypress(function (event) {
            that.onKeyPress(event);
            event.stopPropagation();
        });

        this.$el.focus(function (event) {
            that.onFocus(event);
            event.stopPropagation();
        });

        this.initials.select = function (event) {
            that.onSelect(event);
        };

        if ($.com.hasValue(this.options.dataSource)) {
            this.$el.off('query.' + this.namespace).on('query.' + this.namespace, function (event, args) {
                that.onQuery(event, args);
                event.stopPropagation();
            });
        }

        if ($.com.isJqueryObject(this.$fieldset) && $.com.hasValue(this.options.icon)) {
            this.$el.off('iconClick.' + this.namespace).on('iconClick.' + this.namespace, function (event) {
                that.onIconClick(event);
                event.stopPropagation();
            });

            this.$fieldset.on('click', '.field-group-addon', function (event) {
                that.trigger(event, 'iconClick');
                event.stopPropagation();
            });
        }
    };

    Field.prototype.initFormat = function () {
        if ($.com.hasValue(this.initials.dataType)) {
            this.initials.format = this.initials.format || _config_.format.default[this.initials.dataType];
        }

        if (this.initials.role === 'kendoDatePicker') {
            this.initials.dataType = this.initials.dataType || 'date';
            this.initials.format = this.initials.format || _config_.format.default.date;
        }
        if (this.initials.role === 'kendoTimePicker') {
            this.initials.dataType = this.initials.dataType || 'date';
            this.initials.format = this.initials.format || _config_.format.default.time;
        }
        if (this.initials.role === 'kendoDateTimePicker') {
            this.initials.dataType = this.initials.dataType || 'date';
            this.initials.format = this.initials.format || _config_.format.default.datetime;
        }
    };

    Field.prototype.initUploader = function () {
        if (this.initials.button === 'import') {
            this.initials = $.extend(true, {}, _config_.uploader.import, this.initials);
        } else {
            this.initials = $.extend(true, {}, (this.initials.single ? _config_.uploader.single : _config_.uploader.initials), this.initials);
            if ($.com.hasValue(this.initials.uploadTemplate)) {
                this.initials.uploadTemplate = kendo.template($(this.initials.uploadTemplate).html());
            }
        }

        if (!this.initials.enable) {
            this.initials.editable = false;
            this.initials.deletable = false;
        }

        if (!this.initials.multiple) {
            this.initials.fileCount = 1;
        }

        this.initials.businessType = this.initials.businessType || this.initials.field;
        if ($.com.hasValue(this.initials.fileExtension) && this.initials.enable) {
            this.initials.fileExtension = this.initials.fileExtension.toLowerCase();
            this.$el.attr('accept', this.initials.fileExtension);

            var title = '';
            if (this.initials.fileExtension !== '*') {
                title += '类型限制: ' + this.initials.fileExtension;
            }

            if ($.com.hasValue(this.initials.fileSize) && this.initials.fileSize > 0) {
                var size = this.initials.fileSize;
                if ((size / 1024) >= 1) {
                    size = $.com.toFixed(size / 1024, 2) + 'MB';
                } else {
                    size = size + 'KB';
                }

                title += ($.com.hasValue(title) ? '; ' : '') + '大小限制: ' + size;
            }

            if ($.com.hasValue(this.initials.fileCount) && this.initials.fileCount > 0) {
                title += ($.com.hasValue(title) ? '; ' : '') + '数量限制: ' + this.initials.fileCount + '个';
            }

            this.initials.title = (this.initials.title || '') + ($.com.hasValue(title) ? ' (' + title + ')' : '');

            if (this.initials.layout !== 'horizontal') {
                //水平布局情况下, label 和 title 分开显示, 否则, label 和 title 部分都显示到 label 部分
                this.initials.label += this.initials.title;
            }
        }
    };

    Field.prototype.initValidator = function () {
        //默认验证
        this.options.validator = $.extend(true, this.options.validator, _config_.validator.defaults);

        //多选框组的多选一验证
        if (this.options.required && this.options.withGroup) {
            this.options.validator = $.extend(true, this.options.validator, _config_.validator.groupRequired);
        }

        //最大长度验证
        if ($.com.hasValue(this.options.maxLength)) {
            this.options.validator = $.extend(true, this.options.validator, _config_.validator.maxLength);
        }

        //最短长度验证
        if ($.com.hasValue(this.options.minLength)) {
            this.options.validator = $.extend(true, this.options.validator, _config_.validator.minLength);
        }

        if (this.options.required && this.initials.role === 'kendoUpload') {
            this.options.validator = $.extend(true, this.options.validator, _config_.validator.attachmentRequired);
        }

        var that = this;
        $.each(_config_.validator, function (k, v) {
            if ((k !== 'default' || k !== 'groupRequired') && that.options[k]) {
                that.options.validator = $.extend(true, that.options.validator, v);
            }
        });

        this.validator = this.$el.kendoValidator(this.options.validator).data(_config_.default.kendoValidator);
        if ($.com.hasValue(this.validator)) {
            //记录最原始的验证条件
            this.originals.validator = $.extend(true, {}, this.validator.options);
        }
    };

    Field.prototype.readonly = function (arg) {
        if (!this.isDetail()) {
            this.options.readonly = $.com.hasValue(arg) ? arg : this.options.readonly;
            if ($.com.hasValue(this.options.readonly) && this.options.readonly) {
                this.options.readonly = true;
            } else {
                this.options.readonly = false;
            }

            this.$el.prop('disabled', this.options.readonly);
            this.$el.attr('readonly', this.options.readonly);
            this.$el.attr('data-readonly', this.options.readonly);
            if (this.options.role === 'checkbox' || this.options.role === 'radio') {
                this.$el.find('input:' + this.options.role).prop('disabled', this.options.readonly);
            }

            if ($.com.hasValue(this.role) && !(this.options.role.startsWith('kendo') && this.options.role.endsWith('Picker') || $.com.hasValue(this.options.cascadeFrom)) && $.isFunction(this.role.readonly)) {
                this.role.readonly(this.options.readonly);
            }
        }
    };

    Field.prototype.display = function (arg) {
        this.options.display = $.com.hasValue(arg) ? arg : this.options.display;
        if ($.com.hasValue(this.options.fieldset) && this.options.fieldset) {
            if ($.com.hasValue(this.options.display) && !!this.options.display) {
                this.$fieldset.show();
            } else {
                this.$fieldset.hide();
            }
        } else {
            if ($.com.hasValue(this.options.display) && !!this.options.display) {
                this.$el.show();
            } else {
                this.$el.hide();
            }
        }
    };

    Field.prototype.setOptionsValue = function (value) {
        var formatValue = value;
        if ($.isFunction(this.options.formatter)) {
            formatValue = this.execute(this.options.formatter);
        }

        if (this.options.type === 'file' || this.options.type === 'img') {
            if (this.options.type === 'img' && this.options.single) {
                if ($.com.hasValue(formatValue)) {
                    this.$el.attr('src', _config_.default.attachmentPath + formatValue);
                } else {
                    this.$el.removeAttr('src');
                }
            } else {
                var formData = this.getFormData();
                var withAttachments = $.com.hasValue(this.options.withAttachments) && $.com.hasValue(formData[this.options.dataKeyField]);
                if (withAttachments && $.com.hasValue(formatValue)) {
                    this.options.value = formatValue;
                    if (!this.options.single) {
                        this.options.data = formatValue;
                    }

                    if ($.com.isArray(formatValue)) {
                        if (this.options.type === 'img') {
                            let $viewer = this.$el.append('<ul class="image-viewer"></ul>').children().first();
                            for (var index = 0; index < this.options.data.length; index++) {
                                const element = this.options.data[index];
                                $viewer.append(`<li><img src="${$.com.getImageFileName(element.Path)}" alt="${element.Name}"></li>`);
                            }

                            $viewer.viewer();
                        } else {
                            this.role._renderInitialFiles(this.options.data);
                        }

                        this.execute(this.options.loaded);
                    } else {
                        this.getAttachments(() => this.execute(this.options.loaded));
                    }

                    //if (this.options.type === 'file') {
                    //    var formData = this.getFormData();
                    //    if ($.com.hasValue(formData)) {
                    //        formData[this.options.field] = this.options.value;
                    //    }

                    //    this.initInput();
                    //    this.execute(this.options.recalculate);
                    //}
                }
            }
        } else if (this.isDetail()) {
            this.options.value = value;
            this.$el.html(formatValue || '');
            this.$el.attr('value', formatValue);
        } else if (this.options.type === 'checkbox' || this.options.type === 'radio') {
            this.options.value = value;
            // bool 空值用 'null' 替代
            if (formatValue === null || formatValue === '') {
                formatValue = 'null';
            }

            if (this.options.withGroup) {
                formatValue = formatValue.toString().split(',');
                this.$el.find('input').prop('checked', false);
                for (var i = 0; i < formatValue.length; i++) {
                    this.$el.find('input[' + _config_.attribute.id + '="' + formatValue[i].toString().trim() + '"]').prop('checked', true);
                }
            } else {
                this.$el.find('input').prop('checked', formatValue);
            }

            this.$el.attr('value', formatValue);
        } else {
            this.options.value = value;
            this.$el.val(formatValue);
            if (this.role && $.isFunction(this.role.value)) {
                if (this.options.role.startsWith("kendoMulti") && typeof formatValue === 'string' && !$.com.isNullOrEmpty(formatValue)) {
                    formatValue = formatValue.split(',');
                }

                this.role.value(formatValue || null);
            }

            this.$el.attr('value', formatValue);
        }
    };

    Field.prototype.clear = function () {
        this.options.phase = 'clear';
        if (!this.options.alwaysInitial) {
            if (this.options.type === 'file' || this.options.type === 'img') {
                if (this.options.type === 'file') {
                    this.role.clearAllFiles();

                    var formData = this.getFormData();
                    if ($.com.hasValue(formData)) {
                        formData[this.options.field] = this.options.single ? null : [];
                    }
                } else if (this.options.type === 'img') {
                    this.$el.empty();
                }
            }

            this.setOptionsValue(this.defaults.value);
        }
    };

    Field.prototype.reset = function () {
        this.options.phase = 'reset';
        if (!this.options.alwaysInitial) {
            if (this.options.type === 'file' || this.options.type === 'img') {
                if (this.options.type === 'file') {
                    this.role.clearAllFiles();

                    var formData = this.getFormData();
                    if ($.com.hasValue(formData)) {
                        formData[this.options.field] = this.options.single ? null : [];
                    }
                } else if (this.options.type === 'img') {
                    this.$el.empty();
                }
            }

            this.setOptionsValue(this.originals.value);
        }
    };

    Field.prototype.refresh = function (value, force) {
        this.options.phase = 'refresh';
        var isForce = ($.com.hasValue(force) && force);
        if (this.options.alwaysInitial && !isForce) {
            this.options.value = this.originals.value;
        } else {
            if (value !== undefined) {
                this.options.value = value;
            } else if ($.com.isJqueryObject(this.$form)) {
                var form = this.$form.data(_config_.form.namespace);
                if ($.com.hasValue(form) && $.com.hasValue(form.options) && (!$.com.isNullOrEmpty(form.options.data) || form.options.editable || isForce)) {
                    this.options.value = $.com.getValue(form, 'options.data.' + (this.options.valueFrom || this.field));
                }
            }
        }

        if (!$.com.hasValue(this.options.value) && this.options.initialWhenEmpty) {
            this.options.value = this.originals.value;
        }

        if (this.options.type === 'file') {
            this.role.clearAllFiles();
        } else if (this.options.type === 'img') {
            this.$el.empty();
        }

        this.setOptionsValue(this.options.value);
    };

    Field.prototype.writeBack = function () {
        //回写
        if (this.options.type !== 'file' || this.options.single) {
            if (this.role && this.role.options.role.startsWith("kendoNumeric")) {
                this.options.value = Number(this.options.value) || 0
                if (Number(this.options.value) > (this.role.options.max || 999999999)) { this.options.value = this.role.options.max };
            }

            if ($.com.hasValue(this.options.value)) {
                if (this.options.value instanceof Date) {
                    this.options.value = this.options.value.format()
                } else if (this.options.dataType === 'date' || this.options.dataType === 'time' || this.options.dataType === 'datetime') {
                    this.options.value = new Date(this.options.value).format();
                }
            }

            if (typeof this.options.value !== 'object') {
                this.$el.attr('value', this.options.value);
            }

            //现在回写不验证是否始终使用初始化的选项
            //var value = this.originals.value;
            //if (!this.options.alwaysInitial) {
            //    value = this.options.value;
            //}
            var value = this.options.value;

            //if (!$.com.hasValue(value) || this.options.valid === false) {
            //    value = null;
            //}
            //现在回写时不校验是否验证通过
            if (!$.com.hasValue(value) || value === 'null') {
                value = null;
            } else if (value === 'true' || value === 'false') {
                value = eval(value);
            }

            var formData = this.getFormData();
            if ($.com.hasValue(formData)) {
                //if ($.com.hasValue(value)) {
                //    formData[this.options.field] = value;
                //} else {
                //    delete formData[this.options.field];
                //}

                //现在可以设置到引用类型中
                $.com.setValue(formData, this.options.field, value);
            }
        }
    };

    Field.prototype.getQueryExpression = function (dataSourceType) {
        //针对高级搜索的情况, 多字段, 多逻辑
        this.options.filters = {};
        if ($.com.hasValue(this.options.value)) {
            if ($.com.hasValue(dataSourceType) && dataSourceType.startsWith('odata') && $.com.hasValue(this.options.logic) && $.com.hasValue(this.options.operator) && this.options.logic && this.options.operator) {
                var that = this;
                var dataType = this.options.dataType || 'string';
                var fields = this.options.field.split(',');
                var values = this.options.value;
                if (dataType === 'string') {
                    values = values.toString().replace(/\s/g, ',').replace(/,+/g, ',').split(',');
                    if (values.length === 1) {
                        values = values[0];
                    }
                }

                var filters = [];
                $.each(fields, function (i, field) {
                    var inner;
                    if ($.isArray(values)) {
                        inner = [];
                        $.each(values, function (j, value) {
                            inner.push({
                                field: field,
                                type: dataType,
                                operator: that.options.operator,
                                value: value
                            });
                        });

                        filters.push({
                            filters: inner,
                            logic: that.options.logic
                        });
                    } else {
                        var evalValue = $.com.eval(values);
                        if ((dataType === 'boolean' || dataType === 'number')) {
                            values = evalValue;
                        } else {
                            values = evalValue || values;
                        }

                        if (dataType === 'datetime' || dataType === 'date' || dataType === 'time') {
                            values = new Date(values);
                        }

                        if (dataType === 'string' && typeof values !== 'string') {
                            values = values.toString();
                        }

                        inner = {
                            field: field,
                            type: dataType,
                            operator: that.options.operator,
                            value: values
                        };

                        filters.push(inner);
                    }
                });

                if (filters.length === 1) {
                    this.options.filters = filters;
                } else {
                    this.options.filters = [{
                        filters: filters,
                        logic: this.options.logic
                    }];
                }
            } else {
                this.options.filters[this.options.field] = this.options.value;
            }
        }

        return this.options.filters;
    };

    Field.prototype.getFormData = function () {
        if ($.com.isJqueryObject(this.$form)) {
            var formData = this.$form.data(_config_.form.namespace);
            if ($.com.hasOptionsData(formData)) {
                return formData.options.data;
            }
        }
    };

    Field.prototype.destroy = function () {
        this.options.phase = 'destroy';
        var formData = this.$form.data(_config_.form.namespace);
        if ($.com.hasValue(formData) && formData.$fields.hasValue()) {
            formData.$fields.removeItem(this.$el);
        }

        if ($.com.hasValue(this.role) && $.isFunction(this.role.destroy)) {
            this.role.destroy();
        }

        if ($.com.hasValue(this.options.fieldset) && this.options.fieldset) {
            this.$fieldset.remove();
        }

        this.$el.removeData(_config_.field.namespace);
        this.$el.remove();
    };

    Field.prototype.onClick = function (event) {
        //现在前置事件只需返回 false 就可以阻止事件继续执行
        var ret = this.execute(this.options.clicking, event);
        if ($.com.hasValue(ret) && ret === false) {
            return;
        }

        this.execute(this.options.clicked, event);
    };

    Field.prototype.onQuery = function (event, args) {
        //现在前置事件只需返回 false 就可以阻止事件继续执行
        var ret = this.execute(this.options.querying, event, args);
        if ($.com.hasValue(ret) && ret === false) {
            return;
        }

        if ($.com.hasValue(this.role) && $.com.hasValue(this.role.dataSource)) {
            var dataSource = $.com.getDataSource(this.role);
            var queryOptions = $.com.getQueryOptions(dataSource, args);
            if ($.com.hasValue(queryOptions) && $.isFunction(this.role.dataSource.query))
                this.role.dataSource.query(queryOptions);
        }

        this.execute(this.options.queried, event, args);
    };

    Field.prototype.onFocus = function (event) {
        //现在前置事件只需返回 false 就可以阻止事件继续执行
        var ret = this.execute(this.options.focusing, event);
        if ($.com.hasValue(ret) && ret === false) {
            return;
        }

        if (this.options.role === 'kendoAutoComplete' && this.options.autoQuery) {
            this.role.search();
        }

        this.execute(this.options.focused, event);
    }

    Field.prototype.onIconClick = function (event) {
        //现在前置事件只需返回 false 就可以阻止事件继续执行
        var ret = this.execute(this.options.iconClicking, event);
        if ($.com.hasValue(ret) && ret === false) {
            return;
        }

        //do what?

        this.execute(this.options.iconClicked, event);
    };

    Field.prototype.onChange = function (event) {
        //现在前置事件只需返回 false 就可以阻止事件继续执行
        var ret = this.execute(this.options.changing, event);
        if ($.com.hasValue(ret) && ret === false) {
            return;
        }

        //获取数据
        this.getOptionsValue();
        //验证数据
        this.validate();
        //被触发时回写到kendo组件中, TBC******
        if ($.com.hasValue(this.role) && $.isFunction(this.role.value) && this.options.role !== 'kendoEditor' && $.com.hasValue(this.options.value) && this.options.value !== this.options.selectedValue) {
            this.role.value(this.options.value);
        }
        //数据回写
        this.writeBack();

        if ($.com.hasValue(this.options.next) && $.isArray(this.options.next) && this.options.next.length > 0) {
            var that = this;
            var next = $.extend(true, [], this.options.next);
            for (var i = 0; i < next.length; i++) {
                var x = next[i];
                var target = $(x.target);
                if ($.com.isJqueryObject(target)) {
                    delete x['target'];
                    if ($.com.hasValue(x.filter)) {
                        x.filter = that.setFilterValues(x.filter);
                    }

                    target.trigger('query', x);
                }
            }
        }

        this.execute(this.options.changed, event);
    };

    Field.prototype.onSelect = function (event) {
        //现在前置事件只需返回 false 就可以阻止事件继续执行
        var ret = this.execute(this.options.selecting, event);
        if ($.com.hasValue(ret) && ret === false) {
            return;
        }

        this.options.selectedValue = null;
        if ($.com.hasValue(event) && $.com.hasValue(event.dataItem)) {
            if (this.options.role.startsWith('kendoMulti')) {
                var kendoValue = this.role.value();
                kendoValue.push(event.dataItem[this.options.dataValueField]);
                this.options.selectedValue = kendoValue.join();
            }
            else {
                this.options.selectedValue = event.dataItem[this.options.dataValueField];
            }
        }

        this.execute(this.options.selected, event);
    };

    Field.prototype.onKeyPress = function (event) {
        //现在前置事件只需返回 false 就可以阻止事件继续执行
        var ret = this.execute(this.options.keyPressing, event);
        if ($.com.hasValue(ret) && ret === false) {
            return;
        }

        //退格键不触发keypress,加上change事件性能不好,因此此处不触发change
        //this.onChange(event);

        //回车则执行 form 下 enterPress="true" 的按钮点击事件
        if (event.keyCode === 13) {
            this.getOptionsValue();

            var formData = this.$form.data(_config_.form.namespace);
            if ($.com.hasValue(formData) && $.com.isJqueryObject(formData.$buttons)) {
                var $button = formData.$buttons.filter(_config_.selector.enterPressButton);
                if ($.com.isJqueryObject($button)) {
                    $button.click();
                }
            }

            event.preventDefault();
        }

        this.execute(this.options.keyPressed, event);
    };

    Field.prototype.onKeyUp = function (event) {
        var value = this.$el.val();
        // kendo validator 验证正则表达式时结果不正确, 导致此处无法使用集成正则表达式验证, 此方法待完善
        //if ($.com.hasValue(this.options.pattern)) {
        //    var reg = new RegExp(this.options.pattern, ['i']);
        //    if (!reg.test(value)) {
        //        this.onChange(event);
        //    }
        //}

        if ($.com.hasValue(this.options.maxLength)) {
            try {
                var maxLength = Number(this.options.maxLength);
                if (maxLength > 0 && value.length > maxLength) {
                    value = value.substring(0, maxLength);
                    this.$el.val(value);
                }
            } catch (e) {
                throw new Error(this.options.label + ' 最大长度配置错误, 只能是数字!');
            }
        }
    };

    Field.prototype.getOptionsValue = function () {
        this.options.value = this.$el.val();
        if (this.options.role === 'checkbox' || this.options.role === 'radio') {
            if (this.options.withGroup && $.com.isArray(this.options.data)) {
                var checked = this.$el.find('input:checked');
                if (checked.hasValue()) {
                    var value = [];
                    checked.each(function () {
                        value.push($(this).attr(_config_.attribute.id));
                    });
                    this.options.value = value.join(', ');
                }
            } else {
                this.options.value = this.$el.find('input').prop('checked');
            }
        } else if (this.options.role.startsWith('kendoMulti')) {
            var kendoValue = this.role.value();
            if ($.com.isArray(kendoValue)) {
                this.options.selectedValue = kendoValue.join();
                if ($.com.hasValue(this.options.value) && !this.options.selectedValue.includes(this.options.value)) {
                    kendoValue.push(this.options.value);
                    this.options.selectedValue = kendoValue.join();
                    this.role.value(kendoValue);
                }
            }
            else {
                this.options.selectedValue = null;
            }
        }

        if (this.options.dataTextField !== this.options.dataValueField) {
            if ($.com.hasValue(this.options.selectedValue) && this.options.value !== this.options.selectedValue) {
                this.options.value = this.options.selectedValue;
            }
        }

        if ($.com.hasValue(this.options.value) && typeof this.options.value === 'string') {
            this.options.value = this.options.value.trim();
        }

        return this.options.value;
    };

    //构造单个next对象
    Field.prototype.getSingleNext = function () {
        var $next = $(this.options.next);
        if ($.com.isJqueryObject($next)) {
            var next = $.com.getDataBindFromElement($next);
            if ($.com.hasValue(next)) {
                var filter = $.com.getValue(next, 'dataSource.filter');
                if ($.com.hasValue(filter)) {
                    var field = filter.field;
                    if (!$.com.hasValue(field) && $.isArray(filter.filters) && filter.filters.length > 0) {
                        field = filter.filters[0].field;
                    }

                    this.options.next = [{
                        filter: {
                            filters: [{
                                field: field,
                                operator: 'eq',
                                value: this.options.field
                            }]
                        },
                        target: this.options.next
                    }];
                }
            }
        }
    };

    Field.prototype.setFilterValues = function (filter) {
        if ($.com.hasValue(filter)) {
            if ($.isArray(filter.filters) && filter.filters.length > 0) {
                var that = this;
                $.each(filter.filters, function (i, x) {
                    x = that.setFilterValues(x);
                });
            } else {
                var formData = this.$form.data(_config_.form.namespace);
                if ($.com.hasValue(filter.value) && $.com.hasValue(formData)) {
                    var value = $.com.getValue(formData, 'options.data.' + filter.value);
                    if ($.com.hasValue(value)) {
                        filter.value = value;
                    }
                }
            }
        }

        return filter;
    };

    Field.prototype.validate = function (options) {
        this.options.valid = true;

        if (options !== false) {
            options = $.extend(true, {}, options, this.options.validator);
        }

        if (!$.com.hasValue(this.validator)) {
            var fn = $.com.getFunction(_config_.default.kendoValidatorPlugin);
            if ($.isFunction(fn)) {
                this.validator = this.$el.kendoValidator(options).data(_config_.default.kendoValidator);
            }
        } else {
            this.validator.options = $.extend(true, {}, this.originals.validator, options);
        }

        this.validator.hideMessages();
        if ($.com.isJqueryObject(this.$fieldset)) {
            this.$fieldset.find(_config_.selector.inputInvalidMsg).hide();
        }
        if ($.com.hasValue(this.validator)) {
            if (this.$el.is('[disabled]')) {
                this.$el.removeAttr('disabled');
            }

            if (options !== false && !this.options.validateWhenDisplay) {
                //kendo的规则是只读的组件不校验, 因此, 弹出框, 选择框这一类的组件在校验前先去掉只读样式, 校验完再加上
                var isReadonly = false;
                if (this.options.role === 'checkbox' || this.options.role === 'radio') {
                    isReadonly = this.$el.is(':disabled') || this.$el.is('[readonly]') || this.$el.is('[data-readonly="true"]');
                } else {
                    isReadonly = !this.$el.is(this.validator._inputSelector);
                }

                if (isReadonly) {
                    this.readonly(false);
                }

                this.options.valid = this.validator.validate();

                if (isReadonly) {
                    this.readonly(true);
                }

                if ($.com.isJqueryObject(this.$fieldset)) {
                    this.$fieldset.find(_config_.selector.inputInvalidMsg).find('.k-icon').remove();
                } else if (this.$el.next().is(_config_.selector.inputInvalidMsg)) {
                    this.$el.next().find('.k-icon').remove();
                }
            }

            this.readonly();
        }

        return this.options.valid;
    };

    Field.prototype.isDetail = function () {
        var arg = this.options;
        if (!$.com.hasValue(arg)) {
            arg = this.originals;
        }

        if (!$.com.hasValue(arg)) {
            arg = this.initials;
        }

        return arg.type === 'label' || arg.type === 'span' || arg.type === 'a';
    };

    Field.prototype.getAttachments = function (then) {
        let formData = this.getFormData();
        let id = formData[this.options.dataKeyField];
        let url = `/api/Attachments?businessType=${this.options.businessType}&businessKey=${id}`;
        this.options.url = url;
        $.api.get(this.options.url, data => {
            if ($.isFunction(this.options.dataAdapter)) {
                this.options.data = this.options.dataAdapter(data);
                if (this.options.type === 'file') {
                    this.options.dataSource = { data: this.options.data };
                    this.initials.dataSource = this.options.dataSource;
                }
            }

            if (this.initials.single) {
                if (!$.com.isArray(formData[this.options.withAttachments])) {
                    formData[this.options.withAttachments] = [];
                }

                let attachments = formData[this.options.withAttachments];
                if ($.com.isArray(this.options.data)) {
                    for (var index = 0; index < this.options.data.length; index++) {
                        const element = this.options.data[index];
                        if (!attachments.exist(x => x.Path === element.Path)) {
                            attachments.push(element);
                        }
                    }
                }
            } else {
                formData[this.options.withAttachments] = this.options.data;
            }

            if (this.options.type === 'file') {
                this.role._renderInitialFiles(this.options.data);
            } else if (this.options.type === 'img') {
                let $viewer = this.$el.append('<ul class="image-viewer"></ul>').children().first();
                for (var index = 0; index < this.options.data.length; index++) {
                    const element = this.options.data[index];
                    $viewer.append(`<li><img src="${$.com.getImageFileName(element.Path)}" alt="${element.Name}"></li>`);
                }

                $viewer.viewer();
            }

            if (then && typeof then === 'function') {
                then();
            }
        });
    };

    // plug-in
    $.fn.field = function (options) {
        var args = Array.prototype.slice.call(arguments, 1);
        if (args.length === 0) {
            args = undefined;
        }

        var value = undefined;
        this.each(function () {
            var $this = $(this);
            var data = $this.data(_config_.field.namespace);
            // initial if nover
            if ($.com.isNullOrEmpty(data)) {
                data = new Field(this, typeof options === 'object' && options);
                $this.data(_config_.field.namespace, data);
            }

            if (typeof options === 'string') {
                //if options is not a method
                if ($.inArray(options, data.methods) < 0) {
                    throw 'Unknown method: ' + options;
                }

                //if options is a plugin method, to execute
                value = data[options].apply(data, args);
            }
        });

        return typeof value === 'undefined' ? this : value;
    };
})(jQuery);